import { baseBoard, Board } from './Board';
import { defaultRules, Rules } from './Rules';
import { PieceColor } from './Piece';
import { Move } from './Move';

export interface SavedGame {
    board: string;
    moves: string[];
    currentPlayer: number;
}

export function isSavedGame(obj: any): obj is SavedGame {
    if (obj.board && obj.moves && obj.currentPlayer) {
        return typeof obj.board === 'string'
        && typeof obj.moves === 'object'
        && typeof obj.currentPlayer === 'number';
    } else {
        return false;
    }
}

export class Game {
    private constructor() {
        this.liveBoard = new Board(this);
        this.rulesInstance = new Rules(defaultRules);
    }

    public static newGame(): Game {
        const game = new Game();
        game.initialBoardState = baseBoard;
        game.liveBoard.load(game.initialBoardState);
        return game;
    }

    public static loadGame(savedGame: string): Game {
        const obj = JSON.parse(savedGame);
        if (isSavedGame(obj)) {
            const game = new Game();
            game.initialBoardState = obj.board;
            game.liveBoard.load(game.initialBoardState);
            game.moves = obj.moves;
            game.color = obj.currentPlayer;
            return game;
        } else {
            throw new Error('Not a saved game!');
        }
    }

    public save(): string {
        return JSON.stringify({
            board: this.liveBoard.save(),
            moves: this.moves,
            currentPlayer: this.color
        });
    }

    public makeMove(move: Move): boolean {
        const serializedMove = move.serialize(this.liveBoard);
        if (this.liveBoard.tryMove(move)) {
            this.moves.push(serializedMove);
            this.changeCurrentColor();
            return true;
        }
        return false;
    }

    public revertLastMove() {
        this.changeCurrentColor();
        this.moves.pop();
        this.replay();
    }

    public revertLastTwoMoves() {
        this.moves.splice(this.registeredMoves.length - 2, 2);
        this.replay();
    }

    private changeCurrentColor() {
        if (this.color === PieceColor.RED) {
            this.color = PieceColor.DARK;
        } else if (this.color === PieceColor.DARK) {
            this.color = PieceColor.RED;
        }
    }

    private replay() {
        this.liveBoard = new Board(this);
        this.liveBoard.load(this.initialBoardState);
        for (let i = 0; i < this.moves.length; i++) {
            const move = Move.deserialize(this.liveBoard, this.moves[i]);
            this.liveBoard.movePiece(move.getSourcePiece(this.liveBoard), move.getTargetPiece(this.liveBoard));
        }
    }

    public get rules() {
        return this.rulesInstance;
    }

    public get currentPlayer() {
        return this.color;
    }

    public get registeredMoves() {
        return this.moves;
    }

    public get board() {
        return this.liveBoard;
    }

    private liveBoard: Board;
    private initialBoardState: string = '';
    private moves: string[] = [];
    private rulesInstance: Rules;
    private color: PieceColor = PieceColor.RED;
}
